package cn.anc.aonicardv.widget;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;

import androidx.annotation.Nullable;

public class EVMeterView extends View implements ValueAnimator.AnimatorUpdateListener {

    private Paint mPaint;
    private String[] mValues;
    private float mCurrentAngle;
    private float[] mArcVerticalHeight;
    private int mSelectPosition;
    ObjectAnimator mSlideAnimator;
    private int[] mColors = new int[]{0xFF33BBFF, 0xFF139CE1, 0xFF129B22, 0xFFFFF721, 0xFFFFB745, 0xFFFF7A22};
    private LinearGradient mLinearGradient;
    private RectF mRectF = new RectF();
    public static final int DEFAULT_RING_WIDTH = 20;
    public static final int DEFAULT_VALUE_DISPLAY_SIZE = 100;
    public static final int DEFAULT_PADDING = DEFAULT_VALUE_DISPLAY_SIZE + DEFAULT_RING_WIDTH / 2;
    private Rect mRect = new Rect();
    private OnSelectListener mOnSelectListener;

    public EVMeterView(Context context, String[] values, String current) {
        super(context, null);
        setValues(values, current);
    }

    public EVMeterView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(0xFFD9F6FF);
        mPaint.setStrokeWidth(DEFAULT_RING_WIDTH);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mSlideAnimator = new ObjectAnimator();
        mSlideAnimator.setDuration(680);
        mSlideAnimator.setInterpolator(new AccelerateInterpolator());
        mSlideAnimator.addUpdateListener(this);
    }

    public void setValues(String[] values, String current) {
        mValues = values;
        mSelectPosition = getCurrentPosition(current);
        mArcVerticalHeight = new float[mValues.length / 2];
        startAnimateByPosition(mSelectPosition);
    }

    private int getCurrentPosition(String current) {
        int position = 0;
        for (int i = 0; i < mValues.length; i++) {
            if (mValues[i].equals(current)) {
                position = i;
                break;
            }
        }
        return position;
    }

    public void setOnSelectListener(OnSelectListener onSelectListener) {
        this.mOnSelectListener = onSelectListener;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (getWidth() != getHeight())
            throw new IllegalArgumentException(" Width and height must be equal ! ");
        mPaint.setColor(0xFFD9F6FF);
        mPaint.setStrokeWidth(DEFAULT_RING_WIDTH);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mRectF.setEmpty();
        mRectF.set(DEFAULT_PADDING, DEFAULT_PADDING, getWidth() - DEFAULT_PADDING, getHeight() - DEFAULT_PADDING);
        canvas.drawArc(mRectF, 135, 270, false, mPaint);
        if (mCurrentAngle > 0) {
            if (mLinearGradient == null)
                mLinearGradient = new LinearGradient(DEFAULT_VALUE_DISPLAY_SIZE, DEFAULT_VALUE_DISPLAY_SIZE, getWidth() - DEFAULT_VALUE_DISPLAY_SIZE, getHeight() - DEFAULT_VALUE_DISPLAY_SIZE, mColors, null, LinearGradient.TileMode.CLAMP);
            mPaint.setShader(mLinearGradient);
            canvas.drawArc(mRectF, 135, mCurrentAngle, false, mPaint);
        }
        mPaint.setStrokeWidth(7);
        mPaint.setShader(null);
        float segmentAngle = (float) 270 / (mValues.length - 1);
        float angle = 45;
        int r = (getWidth() - DEFAULT_PADDING * 2) / 2;
        int r2 = r + DEFAULT_RING_WIDTH / 2;
        int cxy = r + DEFAULT_PADDING;
        setArcVerticalHeight(segmentAngle, angle, cxy, r2);
        drawValue(canvas, segmentAngle, r, r2, cxy);
    }

    private void setArcVerticalHeight(float segmentAngle, float angle, int cxy, int r) {
        float lastY = 0;
        for (int i = 0; i < mValues.length / 2 + 1; i++) {
            float x = (float) (cxy - Math.sin(Math.toRadians(angle)) * r);
            float y = (float) (Math.sqrt(Math.pow(r, 2) - Math.pow(x - cxy, 2)) + cxy);
            if (i != 0) {
                mArcVerticalHeight[i - 1] = Math.abs(lastY - y);
            }
            lastY = y;
            angle += segmentAngle;
        }
    }

    private void drawValue(Canvas canvas, float segmentAngle, int r, int r2, int cxy) {
        float angle;
        angle = 45;
        for (int i = 0; i < mValues.length / 2 + 1; i++) {
            float startX1 = (float) (cxy - Math.sin(Math.toRadians(angle)) * r);
            float stopX1 = (float) (cxy - Math.sin(Math.toRadians(angle)) * r2);
            float startX2 = (float) (cxy + Math.sin(Math.toRadians(angle)) * r);
            float stopX2 = (float) (cxy + Math.sin(Math.toRadians(angle)) * r2);
            float stopY1;
            float startY1;
            if (angle > 90) {
                stopY1 = (float) (-Math.sqrt(Math.pow(r2, 2) - Math.pow(stopX1 - cxy, 2)) + cxy);
                startY1 = (float) (-Math.sqrt(Math.pow(r, 2) - Math.pow(startX1 - cxy, 2)) + cxy);
            } else {
                stopY1 = (float) (Math.sqrt(Math.pow(r2, 2) - Math.pow(stopX1 - cxy, 2)) + cxy);
                startY1 = (float) (Math.sqrt(Math.pow(r, 2) - Math.pow(startX1 - cxy, 2)) + cxy);
            }
            mPaint.setColor(0xFFFF7A22);
            mPaint.setStrokeWidth(7);
            mPaint.setStrokeCap(Paint.Cap.SQUARE);
            canvas.drawLine(startX1, startY1, stopX1, stopY1, mPaint);
            canvas.drawLine(startX2, startY1, stopX2, stopY1, mPaint);
            mPaint.setColor(Color.BLACK);
            mPaint.setStrokeWidth(2);
            mPaint.setStyle(Paint.Style.FILL);
            float size = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getContext().getResources().getDisplayMetrics());
            mPaint.setTextSize(size);
            if (i != mValues.length / 2) {
                canvas.drawText(mValues[i], stopX1 - 90, stopY1 - 20, mPaint);
                canvas.drawText(mValues[mValues.length - 1 - i], stopX2 + 30, stopY1 - 20, mPaint);
            } else {
                canvas.drawText(mValues[i], stopX1 - 16, stopY1 - 40, mPaint);
            }
            angle += segmentAngle;
        }
        float size = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 36, getContext().getResources().getDisplayMetrics());
        mPaint.setStrokeWidth(2);
        mPaint.setColor(0xFF139CE1);
        mPaint.setTextSize(size);
        if (mSelectPosition < 0)
            mSelectPosition = 0;
        mRect.setEmpty();
        String value = mValues[mSelectPosition];
        if ("0".equals(value)) {
            value = "+0";
        }
        mPaint.getTextBounds(value, 0, value.length(), mRect);
        canvas.drawText(value, (getWidth() - DEFAULT_PADDING) / 2 - mRect.width() / 4, (getWidth() - DEFAULT_PADDING) / 2 + mRect.height(), mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                int height = 0;
                for (int i = 0; i < mArcVerticalHeight.length; i++) {
                    height += mArcVerticalHeight[i];
                }
                int right = getWidth() - DEFAULT_VALUE_DISPLAY_SIZE;
                int bottom = height + DEFAULT_VALUE_DISPLAY_SIZE;
                if (checkBoundary(event.getX(), event.getY(), right, bottom)) {
                    int total = 0;
                    int segmentLength = 0;
                    float moveDistance;
                    int lastIndex = 0;
                    if (event.getX() <= getWidth() / 2) {
                        moveDistance = (height - event.getY() + DEFAULT_VALUE_DISPLAY_SIZE);
                        if (moveDistance>height) {
                            moveDistance = height;
                        }
                        for (int i = 0; i < mArcVerticalHeight.length; i++) {
                            total += mArcVerticalHeight[i];
                            if (total >= moveDistance) {
                                segmentLength = i + 1;
                                lastIndex = i;
                                break;
                            }
                        }
                    } else {
                        moveDistance = event.getY() - DEFAULT_VALUE_DISPLAY_SIZE;
                        if (moveDistance>height) {
                            moveDistance = height;
                        }
                        for (int i = mArcVerticalHeight.length - 1; i >= 0; i--) {
                            total += mArcVerticalHeight[i];
                            if (total >= moveDistance) {
                                segmentLength = mArcVerticalHeight.length - i;
                                lastIndex = i;
                                break;
                            }
                        }
                    }
                    float outOfDistance = moveDistance - total + mArcVerticalHeight[lastIndex];
                    if (outOfDistance < (mArcVerticalHeight[lastIndex] / 2)) {
                        segmentLength--;
                    }
                    if (event.getX() > getWidth() / 2) {
                        segmentLength += mValues.length / 2;
                    }
                    mSelectPosition = segmentLength;
                    startAnimateByPosition(segmentLength);
                }
                if (mOnSelectListener != null && event.getAction() == MotionEvent.ACTION_UP)
                    mOnSelectListener.onSelect(mSelectPosition);
                break;
        }
        return true;
    }

    private boolean checkBoundary(float x, float y, int right, int bottom) {
        if (y >= DEFAULT_VALUE_DISPLAY_SIZE-20  && y <= bottom && x >= DEFAULT_VALUE_DISPLAY_SIZE && x <= right + 40) {
            return true;
        }
        return false;
    }

    private void startAnimateByPosition(int position) {
        float segmentAngle = (float) 270 / (mValues.length - 1);
        float lastAngle = position * segmentAngle;
        mSlideAnimator.setFloatValues(mCurrentAngle, lastAngle);
        if (!mSlideAnimator.isStarted())
            mSlideAnimator.start();
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mCurrentAngle = (float) animation.getAnimatedValue();
        invalidate();
    }

    public interface OnSelectListener {

        void onSelect(int position);

    }
}
